package admin

import (
	"fmt"
	"math"
	"sort"
	"time"
	"yunj/app/dto"
	"yunj/app/enum/redisenum"
	"yunj/pkg/global"
	"yunj/pkg/yunj/core/redis"
	"yunj/pkg/yunj/core/response"
	"yunj/pkg/yunj/util"

	"github.com/gin-gonic/gin"
)

type Welcome struct {
	Controller
}

func (ctrl *Welcome) Index(ctx *gin.Context) {
	res := &dto.WelcomeResponse{
		SystemName:       global.Config.App.Name,
		CacheRefreshApi:  util.BuildAdminUrl("/welcome/cache-refresh"),
		NumberDataApi:    util.BuildAdminUrl("/welcome/number-data"),
		LineChartDataApi: util.BuildAdminUrl("/welcome/line-chart-data"),
		MapChartDataApi:  util.BuildAdminUrl("/welcome/map-chart-data"),
		BarChartDataApi:  util.BuildAdminUrl("/welcome/bar-chart-data"),
	}
	ctrl.Render(ctx, "welcome/index", res)
}

// 缓存刷新
func (ctrl *Welcome) CacheRefresh(ctx *gin.Context) {
	// 加锁
	lock := redis.NewLock("dashboard.cache.refresh", 60, true)
	defer lock.Release()
	ok, err := lock.Get()
	if err != nil {
		global.Logger.Error(util.FormatError(err, "仪表台缓存刷新异常，锁获取失败！"))
		response.Fail(ctx, "仪表台缓存刷新异常，锁获取失败！")
		return
	}
	if !ok {
		response.Fail(ctx, "当前操作人数过多，请稍后再试！")
		return
	}
	// 清理缓存
	err = redisenum.GroupByValue(redisenum.GROUP_DASHBOARD).DelCache()
	if err == nil {
		response.Success(ctx, nil)
	} else {
		global.Logger.Error(util.FormatError(err, "仪表台缓存刷新异常！"))
		response.Fail(ctx, "仪表台缓存刷新异常！")
	}
}

// 数量数据
func (ctrl *Welcome) NumberData(ctx *gin.Context) {
	data := &dto.WelcomeNumberDataResponse{}
	todayTime := time.Now().In(time.Local)
	yesterdayTime := todayTime.AddDate(0, 0, -1)
	var err error

	// pv
	hourRequestPvByDate := redisenum.KeyByValue(redisenum.KEY_HOUR_REQUEST_PV_BY_DATE)
	// today
	todayPvCache, err := hourRequestPvByDate.SetArgs(&todayTime).GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日PV数据获取异常！"))
		response.Fail(ctx, "今日PV数据获取异常！")
		return
	}
	todayPv, err := util.JsonTo[[]int](todayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日PV缓存数据转换异常！"))
		response.Fail(ctx, "今日PV缓存数据转换异常！")
		return
	}
	// yesterday
	yesterdayPvCache, err := hourRequestPvByDate.SetArgs(&yesterdayTime).GetCache(86400)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日PV数据获取异常！"))
		response.Fail(ctx, "昨日PV数据获取异常！")
		return
	}
	yesterdayPv, err := util.JsonTo[[]int](yesterdayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日PV缓存数据转换异常！"))
		response.Fail(ctx, "昨日PV缓存数据转换异常！")
		return
	}
	data.Pv = ctrl.handleNumberData(todayPv, yesterdayPv)

	// uv
	hourRequestUvByDate := redisenum.KeyByValue(redisenum.KEY_HOUR_REQUEST_UV_BY_DATE)
	// today
	todayUvCache, err := hourRequestUvByDate.SetArgs(&todayTime).GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日UV数据获取异常！"))
		response.Fail(ctx, "今日UV数据获取异常！")
		return
	}
	todayUv, err := util.JsonTo[[]int](todayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日UV缓存数据转换异常！"))
		response.Fail(ctx, "今日UV缓存数据转换异常！")
		return
	}
	// yesterday
	yesterdayUvCache, err := hourRequestUvByDate.SetArgs(&yesterdayTime).GetCache(86400)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日UV数据获取异常！"))
		response.Fail(ctx, "昨日UV数据获取异常！")
		return
	}
	yesterdayUv, err := util.JsonTo[[]int](yesterdayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日UV缓存数据转换异常！"))
		response.Fail(ctx, "昨日UV缓存数据转换异常！")
		return
	}
	data.Uv = ctrl.handleNumberData(todayUv, yesterdayUv)

	// login-uv
	hourLoginUvByDate := redisenum.KeyByValue(redisenum.KEY_HOUR_LOGIN_UV_BY_DATE)
	// today
	todayLoginUvCache, err := hourLoginUvByDate.SetArgs(&todayTime).GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日登录UV数据获取异常！"))
		response.Fail(ctx, "今日登录UV数据获取异常！")
		return
	}
	todayLoginUv, err := util.JsonTo[[]int](todayLoginUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日登录UV缓存数据转换异常！"))
		response.Fail(ctx, "今日登录UV缓存数据转换异常！")
		return
	}
	// yesterday
	yesterdayLoginUvCache, err := hourLoginUvByDate.SetArgs(&yesterdayTime).GetCache(86400)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日登录UV数据获取异常！"))
		response.Fail(ctx, "昨日登录UV数据获取异常！")
		return
	}
	yesterdayLoginUv, err := util.JsonTo[[]int](yesterdayLoginUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日登录UV缓存数据转换异常！"))
		response.Fail(ctx, "昨日登录UV缓存数据转换异常！")
		return
	}
	data.LoginUv = ctrl.handleNumberData(todayLoginUv, yesterdayLoginUv)

	response.Success(ctx, data)
}

// 折线图数据
func (ctrl *Welcome) LineChartData(ctx *gin.Context) {
	data := &dto.WelcomeLineChartDataResponse{}
	todayTime := time.Now().In(time.Local)
	yesterdayTime := todayTime.AddDate(0, 0, -1)
	var err error

	// pv
	hourRequestPvByDate := redisenum.KeyByValue(redisenum.KEY_HOUR_REQUEST_PV_BY_DATE)
	// today
	todayPvCache, err := hourRequestPvByDate.SetArgs(&todayTime).GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日PV数据获取异常！"))
		response.Fail(ctx, "今日PV数据获取异常！")
		return
	}
	todayPv, err := util.JsonTo[[]int](todayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日PV缓存数据转换异常！"))
		response.Fail(ctx, "今日PV缓存数据转换异常！")
		return
	}
	// yesterday
	yesterdayPvCache, err := hourRequestPvByDate.SetArgs(&yesterdayTime).GetCache(86400)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日PV数据获取异常！"))
		response.Fail(ctx, "昨日PV数据获取异常！")
		return
	}
	yesterdayPv, err := util.JsonTo[[]int](yesterdayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日PV缓存数据转换异常！"))
		response.Fail(ctx, "昨日PV缓存数据转换异常！")
		return
	}
	data.Pv = &dto.WelcomeLineChartDataItem{TodaySeriesData: todayPv, YesterdaySeriesData: yesterdayPv}

	// uv
	hourRequestUvByDate := redisenum.KeyByValue(redisenum.KEY_HOUR_REQUEST_UV_BY_DATE)
	// today
	todayUvCache, err := hourRequestUvByDate.SetArgs(&todayTime).GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日UV数据获取异常！"))
		response.Fail(ctx, "今日UV数据获取异常！")
		return
	}
	todayUv, err := util.JsonTo[[]int](todayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日UV缓存数据转换异常！"))
		response.Fail(ctx, "今日UV缓存数据转换异常！")
		return
	}
	// yesterday
	yesterdayUvCache, err := hourRequestUvByDate.SetArgs(&yesterdayTime).GetCache(86400)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日UV数据获取异常！"))
		response.Fail(ctx, "昨日UV数据获取异常！")
		return
	}
	yesterdayUv, err := util.JsonTo[[]int](yesterdayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "昨日UV缓存数据转换异常！"))
		response.Fail(ctx, "昨日UV缓存数据转换异常！")
		return
	}
	data.Uv = &dto.WelcomeLineChartDataItem{TodaySeriesData: todayUv, YesterdaySeriesData: yesterdayUv}

	response.Success(ctx, data)
}

// 地图数据
func (ctrl *Welcome) MapChartData(ctx *gin.Context) {
	data := &dto.WelcomeMapChartDataResponse{}
	var err error

	// pv
	todayProvRequestPv := redisenum.KeyByValue(redisenum.KEY_TODAY_PROV_REQUEST_PV)
	// today
	todayPvCache, err := todayProvRequestPv.GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省PV数据获取异常！"))
		response.Fail(ctx, "今日各省PV数据获取异常！")
		return
	}
	todayPv, err := util.JsonTo[map[string]int](todayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省PV缓存数据转换异常！"))
		response.Fail(ctx, "今日各省PV缓存数据转换异常！")
		return
	}
	data.Pv = ctrl.handleMapChartData(todayPv)

	// uv
	todayProvRequestUv := redisenum.KeyByValue(redisenum.KEY_TODAY_PROV_REQUEST_UV)
	// today
	todayUvCache, err := todayProvRequestUv.GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省UV数据获取异常！"))
		response.Fail(ctx, "今日各省UV数据获取异常！")
		return
	}
	todayUv, err := util.JsonTo[map[string]int](todayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省UV缓存数据转换异常！"))
		response.Fail(ctx, "今日各省UV缓存数据转换异常！")
		return
	}
	data.Uv = ctrl.handleMapChartData(todayUv)

	response.Success(ctx, data)
}

// 柱状图数据
func (ctrl *Welcome) BarChartData(ctx *gin.Context) {
	data := &dto.WelcomeBarChartDataResponse{}
	var err error

	// pv
	todayProvRequestPv := redisenum.KeyByValue(redisenum.KEY_TODAY_PROV_REQUEST_PV)
	// today
	todayPvCache, err := todayProvRequestPv.GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省PV数据获取异常！"))
		response.Fail(ctx, "今日各省PV数据获取异常！")
		return
	}
	todayPv, err := util.JsonTo[map[string]int](todayPvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省PV缓存数据转换异常！"))
		response.Fail(ctx, "今日各省PV缓存数据转换异常！")
		return
	}
	data.Pv = ctrl.handleBarChartData(todayPv)

	// uv
	todayProvRequestUv := redisenum.KeyByValue(redisenum.KEY_TODAY_PROV_REQUEST_UV)
	// today
	todayUvCache, err := todayProvRequestUv.GetCache(1800)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省UV数据获取异常！"))
		response.Fail(ctx, "今日各省UV数据获取异常！")
		return
	}
	todayUv, err := util.JsonTo[map[string]int](todayUvCache)
	if err != nil {
		global.Logger.Error(util.FormatError(err, "今日各省UV缓存数据转换异常！"))
		response.Fail(ctx, "今日各省UV缓存数据转换异常！")
		return
	}
	data.Uv = ctrl.handleBarChartData(todayUv)

	response.Success(ctx, data)
}

// 处理数量数据
func (ctrl *Welcome) handleNumberData(todayData, yesterdayData []int) *dto.WelcomeNumberDataItem {
	value := 0
	currHour := time.Now().In(time.Local).Hour()
	for i := 0; i <= currHour; i++ {
		if util.SliceIndexExists(todayData, i) {
			value += todayData[i]
		}
	}
	// 昨日同时段数据
	yesterdaySameTimeValue := 0
	for i := 0; i <= currHour; i++ {
		if util.SliceIndexExists(yesterdayData, i) {
			yesterdaySameTimeValue += yesterdayData[i]
		}
	}

	trendFlag := ""
	trendRatio := ""
	if value > 0 && yesterdaySameTimeValue > 0 && value != yesterdaySameTimeValue {
		if value > yesterdaySameTimeValue {
			trendFlag = "rise"
		} else {
			trendFlag = "fall"
		}
		trendRatio = fmt.Sprintf("%.2f", math.Abs(float64(value-yesterdaySameTimeValue))/float64(yesterdaySameTimeValue))
	}
	return &dto.WelcomeNumberDataItem{
		Value: value,
		Trend: &dto.WelcomeNumberDataItemTrend{
			Flag:  trendFlag,
			Ratio: trendRatio,
		},
	}
}

// 处理地图数据
func (ctrl *Welcome) handleMapChartData(data map[string]int) *dto.WelcomeMapChartDataItem {
	legendMin := 1
	legendMax := 0
	total := 0
	values := make([]*dto.WelcomeMapChartDataItemValue, 0, len(data))
	for k, v := range data {
		total += v
		if v > legendMax {
			legendMax = v
		}
		if v < legendMin {
			legendMin = v
		}
		values = append(values, &dto.WelcomeMapChartDataItemValue{
			Name:  k,
			Value: v,
		})
	}
	if legendMin > legendMax {
		legendMin = legendMax
	}
	return &dto.WelcomeMapChartDataItem{
		Legend: &dto.WelcomeMapChartDataItemlegend{
			Max: legendMax,
			Min: legendMin,
		},
		Total:  total,
		Values: values,
	}
}

// 处理柱状图数据
func (ctrl *Welcome) handleBarChartData(data map[string]int) *dto.WelcomeBarChartDataItem {
	yAxisData := make([]string, 0, len(data))
	values := make([]int, len(data))
	total := 0
	for k, v := range data {
		if v > 0 {
			yAxisData = append(yAxisData, k)
			total += v
		}
	}
	// 按值对键进行排序（升序）
	sort.Slice(yAxisData, func(i, j int) bool {
		return data[yAxisData[i]] < data[yAxisData[j]]
	})
	// 生成排序后的值切片
	for i, k := range yAxisData {
		values[i] = data[k]
	}
	return &dto.WelcomeBarChartDataItem{
		Total:     total,
		Values:    values,
		YAxisData: yAxisData,
	}
}
